/*
 * Copyright (C) 2012 Rothens
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package rpg;

import java.util.Random;
import rpg.maps.Map;
import rpg.maps.MapTile;

/**
 * rothens.tarhely.biz
 *
 * @author Rothens
 */
public class Location {

	public static final int UP = 0;
	public static final int LEFT = 1;
	public static final int DOWN = 2;
	public static final int RIGHT = 3;
        public static final int AOE=4;
	private int x, y, tileX, tileY;
	private Map map;

	/**
	 *
	 * @param x the X coordinate
	 * @param y the Y coordinate
	 * @param tile if true, it's a tile-coordinate, otherwise it's
	 * pixel-coordinate
	 */
	public Location(int x, int y, boolean tile, Map map) {
		if (tile) {
			this.x = x * 64;
			this.y = y * 64;
			tileX = x;
			tileY = y;
		} else {
			this.x = x;
			this.y = y;
			tileX = x / 64;
			tileY = y / 64;
		}
		this.map = map;
	}

	public Location(int mapCoordinate, Map map) {
		tileX = mapCoordinate % map.getWidth();
		tileY = mapCoordinate / map.getWidth();

		//These are the center of the given coordinate
		x = tileX * 64 + 32;
		y = tileY * 64 + 30;
		this.map = map;
	}

	public boolean isValid(Map map) {
		return (tileY * map.getWidth() + tileX < map.getWidth() * map.getHeight());
	}

	public Map getMap() {
		return map;
	}

	public void setMap(Map map) {
		this.map = map;
	}

	public int getX() {
		return x;
	}

	public int getY() {
		return y;
	}

	public int getTileX() {
		return tileX;
	}

	public int getTileY() {
		return tileY;
	}

	public void transX(int amt) {
		x += amt;
	}

	public void transY(int amt) {
		y += amt;
	}
        
        public int getExactLocation(){
            return new Location(x,y,false,map).getMapCoordinate();
        }

	public boolean move(int dir, int amt) {
		boolean ret = true;
		int dist = 8;
		Location lower = new Location(x, y + 32, false, map);
		int toCheck;
		switch (dir) {
			case UP:
				toCheck = getTile(UP);
				MapTile tile = (toCheck == -1) ? null : map.tiles[toCheck];
				if (tile != null) {
					if ((!tile.isPassable() || !map.isPassableEntity(toCheck)) && y % 64 < amt + dist) {
						ret = false;

					} else {
						y -= amt;
					}
				} else {
					if (y - amt < dist) {
						y = dist;
						ret = false;
					} else {
						y -= amt;
					}
				}
				break;
			case DOWN:
				toCheck = getTile(DOWN);
				tile = (toCheck == -1) ? null : map.tiles[toCheck];
				if (tile != null) {

					if ((!tile.isPassable() || !map.isPassableEntity(toCheck)) && y % 64 + amt + 64/*
						 * TODO: aktuális entity magasságának a fele!!!
						 */ / 2 >= 64) {
						ret = false;
					} else {
						y += amt;
					}
				} else {
					if (y + amt > map.getHeight() * 64 - dist - 32) {
						y = map.getHeight() * 64 - dist - 32;
						ret = false;
					} else {
						y += amt;
					}
				}
				break;

			case LEFT:
				toCheck = getTile(LEFT);
				tile = (toCheck == -1) ? null : map.tiles[toCheck];

				int lowerCheck = lower.getTile(LEFT);
				MapTile l = (lowerCheck == -1) ? null : map.tiles[lowerCheck];
				if (tile != null) {
					if ((!tile.isPassable() || !l.isPassable() || !map.isPassableEntity(toCheck) || !map.isPassableEntity(lowerCheck)) && x % 64 - dist <= amt) {
						ret = false;
					} else {
						x = (x - amt < dist) ? dist : x - amt;
					}
				} else {
					if (x - amt < dist) {
						x = dist;
						ret = false;
					} else {
						x -= amt;
					}
				}
				break;
			case RIGHT:
				toCheck = getTile(RIGHT);
				tile = (toCheck == -1) ? null : map.tiles[toCheck];

				lowerCheck = lower.getTile(RIGHT);
				l = (lowerCheck == -1) ? null : map.tiles[lowerCheck];
				if (tile != null) {
					if ((!tile.isPassable() || !l.isPassable() || !map.isPassableEntity(toCheck) || !map.isPassableEntity(lowerCheck)) && x % 64 + amt + dist >= 64) {
						ret = false;
					} else {
						x = (x + amt + dist > map.getWidth() * 64) ? map.getWidth() * 64 - dist : x + amt;
					}
				} else {
					if (x + amt + dist > map.getWidth() * 64) {
						ret = false;
						x = map.getWidth() * 64 - dist;
					} else {
						x += amt;
					}
				}
				break;
		}
		tileX = x / 64;
		tileY = y / 64;
		return ret;
	}

	/**
	 * Gets the mapcoordinate location of the given location.
	 *
	 * @param loc the location
	 * @param map the map
	 * @return an integer which is the index of the tile in the array
	 */
	public int getMapCoordinate(Location loc) {
		return (loc.tileY * map.getWidth() + loc.tileX);
	}

	/**
	 *
	 * @return the mapcoordinate of the current location
	 */
	public int getMapCoordinate() {
		return (tileY * map.getWidth() + tileX);
	}

	public double getDistance(Location loc) {
		return Math.sqrt(Math.pow(x - loc.x, 2) + Math.pow(y - loc.y, 2));
	}

	public int getTile(int dir) {
		int mapcoord = 0;
                //Add 20 for direction 
		switch (dir) {
			case UP:
				if (tileY == 0) {
					return -1;
				}                                             
				mapcoord = getMapCoordinate(new Location(tileX, tileY-1, true, map));
				
                                break;
			case DOWN:
				if (tileY == map.getHeight() - 1) {
					return -1;
				}
				mapcoord = getMapCoordinate(new Location(tileX, tileY + 1, true, map));
				break;
			case LEFT:
				if (tileX == 0) {
					return -1;
				}
				mapcoord = getMapCoordinate(new Location(tileX - 1, tileY, true, map));
				break;
			case RIGHT:
				if (tileX == map.getWidth() - 1) {
					return -1;
				}
				mapcoord = getMapCoordinate(new Location(tileX + 1, tileY, true, map));
				break;
		}
		return mapcoord;
		//return map.tiles[mapcoord];
	}
        
        

	public Location getCentered() {
		x = tileX * 64 + 32;
		y = tileY * 64 + 32;
		return this;
	}

	public int getDir(Location loc) {
		int chX = loc.getX();
		int chY = loc.getY();
		int xDiff = Math.abs(chX - x);
		int yDiff = Math.abs(chY - y);
		if (xDiff == 0 && yDiff == 0) {
			return -1;
		}
		if (xDiff > yDiff) {
			if (chX > x) {
				return RIGHT;
			} else {
				return LEFT;
			}
		} else {
			if (chY > y) {
				return DOWN;
			} else {
				return UP;
			}
		}

	}

	public Location getPossibleLocation(Map map) {
		for (int i = 0; i < 4; i++) {
			Random r = new Random();
			int rnd = r.nextInt(4);
			int tileLoc = getTile(rnd);
			if (tileLoc != -1) {
				Location l = new Location(tileLoc, map);
				if (rnd == UP || rnd == DOWN) {
					l.x = x;
				} else {
					l.y = y;
				}
				return l;
			}
		}
		return this;
	}

	public Location normalize(Location to) {
		int dir = getDir(to);
		if (dir == UP || dir == DOWN) {
			x = to.x;
		} else {
			y = to.y;
		}
		return this;
	}

	public boolean equals(Location loc) {
		return x == loc.x && y == loc.y;
	}
	
	public boolean equalTile(Location loc){
		return tileX == loc.tileX && tileY == loc.tileY;
	}

	public boolean equals(Location loc, int diff) {
		int h = diff / 2;
		return Math.abs(loc.x - x) <= h && Math.abs(loc.y - y) <= h;
	}
}